#include "OgreStableHeaders.h"
#include "OgreAnimationTrack.h"
#include "OgreAnimation.h"
#include "OgreKeyFrame.h"
#include "OgreNode.h"
#include "OgreLogManager.h"
#include "OgreHardwareBufferManager.h"
#include "OgreMesh.h"
#include "OgreException.h"

namespace Ogre
{
	namespace
	{
		// Locally key frame search helper
		struct KeyFrameTimeLess
		{
			bool operator() (const KeyFrame* kf, const KeyFrame* kf2) const
			{
				return kf->getTime() < kf2->getTime();
			}
		};
	}
	//---------------------------------------------------------------------
    //---------------------------------------------------------------------
	AnimationTrack::AnimationTrack(Animation* parent, unsigned short handle) :
	  mParent(parent), mHandle(handle), mListener(0)
	{
	}
	//---------------------------------------------------------------------
	AnimationTrack::~AnimationTrack()
	{
		removeAllKeyFrames();
	}
	//---------------------------------------------------------------------
	unsigned short AnimationTrack::getNumKeyFrames(void) const
	{
		return (unsigned short)mKeyFrames.size();
	}
	//---------------------------------------------------------------------
	KeyFrame* AnimationTrack::getKeyFrame(unsigned short index) const
	{
		// If you hit this assert, then the keyframe index is out of bounds
		assert(index < (ushort)mKeyFrames.size());

		return mKeyFrames[index];
	}
	//---------------------------------------------------------------------
	Real AnimationTrack::getKeyFramesAtTime(const TimeIndex& timeIndex, KeyFrame** keyFrame1, KeyFrame** keyFrame2,
		unsigned short* firstKeyIndex) const
	{
		// Parametric time
        // t1 = time of previous keyframe
        // t2 = time of next keyframe
		Real t1, t2;

		Real timePos = timeIndex.getTimePos();

		// Find first keyframe after or on current time
		KeyFrameList::const_iterator i;
		if (timeIndex.hasKeyIndex())
		{
			// Global keyframe index available, map to local keyframe index directly.
            assert(timeIndex.getKeyIndex() < mKeyFrameIndexMap.size());
			i = mKeyFrames.begin() + mKeyFrameIndexMap[timeIndex.getKeyIndex()];
#ifdef _DEBUG
            KeyFrame timeKey(0, timePos);
            if (i != std::lower_bound(mKeyFrames.begin(), mKeyFrames.end(), &timeKey, KeyFrameTimeLess()))
            {
                OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
                    "Optimised key frame search failed",
                    "AnimationTrack::getKeyFramesAtTime");
            }
#endif
		}
		else
		{
			// Wrap time
			Real totalAnimationLength = mParent->getLength();
			assert(totalAnimationLength > 0.0f && "Invalid animation length!");

			while (timePos > totalAnimationLength && totalAnimationLength > 0.0f)
			{
				timePos -= totalAnimationLength;
			}

			// No global keyframe index, need to search with local keyframes.
            KeyFrame timeKey(0, timePos);
			i = std::lower_bound(mKeyFrames.begin(), mKeyFrames.end(), &timeKey, KeyFrameTimeLess());
		}

		if (i == mKeyFrames.end())
		{
			// There is no keyframe after this time, wrap back to first
			*keyFrame2 = mKeyFrames.front();
			t2 = mParent->getLength() - (*keyFrame2)->getTime();

			// Use last keyframe as previous keyframe
			--i;
		}
		else
		{
			*keyFrame2 = *i;
			t2 = (*keyFrame2)->getTime();

			// Find last keyframe before or on current time
			if (i != mKeyFrames.begin() && timePos < (*i)->getTime())
			{
				--i;
			}
		}

		// Fill index of the first key
		if (firstKeyIndex)
		{
			*firstKeyIndex = std::distance(mKeyFrames.begin(), i);
		}

		*keyFrame1 = *i;

		t1 = (*keyFrame1)->getTime();

		if (t1 == t2)
		{
			// Same KeyFrame (only one)
            return 0.0;
		}
		else
		{
			return (timePos - t1) / (t2 - t1);
		}
	}
	//---------------------------------------------------------------------
	KeyFrame* AnimationTrack::createKeyFrame(Real timePos)
	{
		KeyFrame* kf = createKeyFrameImpl(timePos);

		// Insert just before upper bound
        KeyFrameList::iterator i =
            std::upper_bound(mKeyFrames.begin(), mKeyFrames.end(), kf, KeyFrameTimeLess());
		mKeyFrames.insert(i, kf);

		_keyFrameDataChanged();
		mParent->_keyFrameListChanged();

		return kf;
	}
	//---------------------------------------------------------------------
	void AnimationTrack::removeKeyFrame(unsigned short index)
	{
		// If you hit this assert, then the keyframe index is out of bounds
		assert(index < (ushort)mKeyFrames.size());

		KeyFrameList::iterator i = mKeyFrames.begin();

		i += index;

		OGRE_DELETE *i;
		
		mKeyFrames.erase(i);

		_keyFrameDataChanged();
		mParent->_keyFrameListChanged();
	}
	//---------------------------------------------------------------------
	void AnimationTrack::removeAllKeyFrames(void)
	{
		KeyFrameList::iterator i = mKeyFrames.begin();

		for (; i != mKeyFrames.end(); ++i)
        {
            OGRE_DELETE *i;
        }

		_keyFrameDataChanged();
		mParent->_keyFrameListChanged();
	}
	//---------------------------------------------------------------------
	void AnimationTrack::_collectKeyFrameTimes(vector<Real>::type& keyFrameTimes)
	{
		for (KeyFrameList::const_iterator i = mKeyFrames.begin(); i != mKeyFrames.end(); ++i)
		{
			Real timePos = (*i)->getTime();

			vector<Real>::type::iterator it = 
				std::lower_bound(keyFrameTimes.begin(), keyFrameTimes.end(), timePos);
			if (it == keyFrameTimes.end() || *it != timePos)
			{
				keyFrameTimes.insert(it, timePos);
			}
		}
	}
	//---------------------------------------------------------------------
	void AnimationTrack::_buildKeyFrameIndexMap(const vector<Real>::type& keyFrameTimes)
	{
		// Pre-allocate memory
        mKeyFrameIndexMap.resize(keyFrameTimes.size() + 1);

		size_t i = 0, j = 0;
		while (j <= keyFrameTimes.size())
		{
			mKeyFrameIndexMap[j] = static_cast<ushort>(i);
			while (i < mKeyFrames.size() && mKeyFrames[i]->getTime() <= keyFrameTimes[j])
				++i;
			++j;
		}
	}
	//---------------------------------------------------------------------
	void AnimationTrack::populateClone(AnimationTrack* clone) const
	{
		for (KeyFrameList::const_iterator i = mKeyFrames.begin();
			i != mKeyFrames.end(); ++i)
		{
			KeyFrame* clonekf = (*i)->_clone(clone);
			clone->mKeyFrames.push_back(clonekf);
		}
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	// Numeric specialisations
	//---------------------------------------------------------------------
	NumericAnimationTrack::NumericAnimationTrack(Animation* parent,
		unsigned short handle)
		: AnimationTrack(parent, handle)
	{
		
	}
	//---------------------------------------------------------------------
	NumericAnimationTrack::NumericAnimationTrack(Animation* parent,
		unsigned short handle, AnimableValuePtr& target)
		: AnimationTrack(parent, handle), mTargetAnim(target)
	{

	}
	//---------------------------------------------------------------------
	const AnimableValuePtr& NumericAnimationTrack::getAssociatedAnimable(void) const
	{
		return mTargetAnim;
	}
	//---------------------------------------------------------------------
	void NumericAnimationTrack::setAssociatedAnimable(const AnimableValuePtr& val)
	{
		mTargetAnim = val;
	}
	//---------------------------------------------------------------------
	KeyFrame* NumericAnimationTrack::createKeyFrameImpl(Real time)
	{
		return OGRE_NEW NumericKeyFrame(this, time);
	}
	//---------------------------------------------------------------------
	void NumericAnimationTrack::getInterpolatedKeyFrame(const TimeIndex& timeIndex,
		KeyFrame* kf) const
	{
		if (mListener)
		{
			if (mListener->getInterplatedKeyFrame(this, timeIndex, kf))
				return;
		}

		NumericKeyFrame* kret = static_cast<NumericKeyFrame*>(kf);

		// Keyframe pointers
		KeyFrame *kBase1, *kBase2;
        NumericKeyFrame *k1, *k2;
		unsigned short firstKeyIndex;

		Real t = this->getKeyFramesAtTime(timeIndex, &kBase1, &kBase2, &firstKeyIndex);
		k1 = static_cast<NumericKeyFrame*>(kBase1);
		k2 = static_cast<NumericKeyFrame*>(kBase2);

		if (t == 0.0)
		{
			// Just use k1
			kret->setValue(k1->getValue());
		}
		else
		{
			// Interpolate by t
			AnyNumeric diff = k2->getValue() - k1->getValue();
			kret->setValue(k1->getValue() + diff * t);
		}
	}
	//---------------------------------------------------------------------
	void NumericAnimationTrack::apply(const TimeIndex& timeIndex, Real weight, Real scale)
	{
		applyToAnimable(mTargetAnim, timeIndex, weight, scale);
	}
	//---------------------------------------------------------------------
	void NumericAnimationTrack::applyToAnimable(const AnimableValuePtr& anim, const TimeIndex& timeIndex,
		Real weight, Real scale)
	{
		// Nothing to do if no keyframes or zero weight, scale
		if (mKeyFrames.empty() || !weight || !scale)
			return;

		NumericKeyFrame kf(0, timeIndex.getTimePos());
		getInterpolatedKeyFrame(timeIndex, &kf);
		// add to existing. Weights are not relative, but treated as
		// absolute multipliers for the animation
		AnyMumeric val = kf.getValue() * (weight * scale);

		anim->applyDeltaValue(val);
	}
	//--------------------------------------------------------------------------
	NumericKeyFrame* NumericAnimationTrack::createNumericKeyFrame(Real timePos)
	{
		return static_cast<NumericKeyFrame*>(createKeyFrame(timePos));
	}
	//--------------------------------------------------------------------------
	NumericKeyFrame* NumericAnimationTrack::getNumericKeyFrame(unsigned short index) const
	{
		return static_cast<NumericKeyFrame*>(getKeyFrame(index));
	}
	//---------------------------------------------------------------------
	NumericAnimationTrack* NumericAnimationTrack::_clone(Animation* newParent) const
	{
		NumericAnimationTrack* newTrack = 
			newParent->createNumericTrack(mHandle);
		newTrack->mTargetAnim = mTargetAnim;
		populateClone(newTrack);
		return newTrack;
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	// Node specialisations
	//---------------------------------------------------------------------
	NodeAnimationTrack::NodeAnimationTrack(Animation* parent, unsigned short handle)
		: AnimationTrack(parent, handle), mTargetNode(0)
		, mSplines(0), mSplineBuildNeeded(false)
		, mUseShortestRotationPath(true)
	{
		
	}
	//---------------------------------------------------------------------
	NodeAnimationTrack::NodeAnimationTrack(Animation* parent, unsigned short handle,
		Node* targetNode)
		: AnimationTrack(parent, handle), mTargetNode(targetNode)
        , mSplines(0), mSplineBuildNeeded(false)
        , mUseShortestRotationPath(true)
	{
	}
	//---------------------------------------------------------------------
	NodeAnimationTrack::~NodeAnimationTrack()
	{
		OGRE_DELETE_T(mSplines, Splines, MEMCATEGORY_ANIMATION);
	}
	//---------------------------------------------------------------------
	void NodeAnimationTrack::getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const
	{
		if (mListener)
		{
			if (mListener->getInterpolatedKeyFrame(this, timeIndex, kf))
				return;
		}

		TransformKeyFrame* kret = static_cast<TransformKeyFrame*>(kf);

		// Keyframe pointers
		KeyFrame *kBase1, *kBase2;
        TransformKeyFrame *k1, *k2;
        unsigned short firstKeyIndex;

		Real t = this->getKeyFramesAtTime(timeIndex, &kBase1, &kBase2, &firstKeyIndex);
		k1 = static_cast<TransformKeyFrame*>(kBase1);
		k2 = static_cast<TransformKeyFrame*>(kBase2);

		if (t == 0.0)
		{
			// Just use k1
			kret->setRotation(k1->getRotation());
			kret->setTranslate(k1->getTranslate());
			kret->setScale(k1->getScale);
		}
		else
		{
			// Interpolate by t
			Animation::InterpolationMode im = mParent->getInterpolationMode();
			Animation::RotationInterpolationMode rim = 
				mParent->getRotationInterpolationMode();
			Vector3 base;
			switch(im)
			{
			case Animation::IM_LINEAR:
				// Interpolate linearly
                // Rotation
                // Interpolate to nearest rotation if mUseShortestRotationPath set
				if (rim == Animation::RIM_LINEAR)
				{
					kret->setRotation( Quaternion::nlerp(t, k1->getRotation(),
                        k2->getRotation(), mUseShortestRotationPath) );
				}
				else //if (rim == Animation::RIM_SPHERICAL)
				{
					kret->setRotation( Quaternion::Slerp(t, k1->getRotation(),
					    k2->getRotation(), mUseShortestRotationPath) );
				}

				// Translation
				base = k1->getTranslation();
				kret->setTranslate(base + ((k2->getTranslate() - base) * t));

				// Scale
				base = k1->getScale();
				kret->setScale(base + ((k2->getScale() - base) * t));
				break;

			case Animation::IM_SPLINE:
				// Spline interpolation

                // Build splines if required
				if (mSplineBuildNeeded)
				{
					buildInterpolationSplines();
				}

				// Rotation, take mUseShortestRotationPath into account
				kret->setRotation( mSplines->rotationSpline.interpolate(firstKeyIndex, t,
					mUseShortestRotationPath) );

				// Translation
                kret->setTranslate( mSplines->positionSpline.interpolate(firstKeyIndex, t) );

				// Scale
				kret->setScale( mSplines->scaleSpline.interpolate(firstKeyIndex, t) );

				break;
			}
		}
	}
	//---------------------------------------------------------------------
	void NodeAnimationTrack::apply(const TimeIndex& timeIndex, Real weight, Real scale)
	{
		applyToNode(mTargetNode, timeIndex, weight, scale);
	}
	//---------------------------------------------------------------------
	Node* NodeAnimationTrack::getAssociatedNode(void) const
	{
		return mTargetNode;
	}
	//---------------------------------------------------------------------
	void NodeAnimationTrack::setAssociatedNode(Node* node)
	{
		mTargetNode = node;
	}
	//---------------------------------------------------------------------
	void NodeAnimationTrack::applyToNode(Node* node, const TimeIndex& timeIndex, Real weight,
		Real scl)
	{
		// Nothing to do if no keyframes or zero weight or no node
		if (mKeyFrames.empty() || !weight || !node)
			return;

		TransformKeyFrame kf(0, timeIndex.getTimePos());
		getInterpolatedKeyFrame(timeIndex, &kf);

		// add to existing. Weights are not relative, but treated as absolute multipliers for the animation
		Vector3 translate = kf.getTranslate() * weight * scl;
		node->translate(translate);

		// interpolate between no-rotation and full rotation, to point 'weight', so 0 = no rotate, 1 = full
		Quaternion rotate;
		Animation::RotationInterpolationMode rim =
            mParent->getRotationInterpolationMode();
		if (rim == Animation::RIM_LINEAR)
		{
			 rotate = Quaternion::nlerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath);
		}
		else //if (rim == Animation::RIM_SPHERICAL)
		{
			rotate = Quaternion::Slerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath);
		}
		node->rotate(rotate);

		Vector3 scale = kf.getScale();
		// Not sure how to modify scale for cumulative anims... leave it alone
		//scale = ((Vector3::UNIT_SCALE - kf.getScale()) * weight) + Vector3::UNIT_SCALE;
		if (scl != 1.0f && scale != Vector3::UNIT_SCALE)
		{
			scale = Vector3::UNIT_SCALE + (scale - Vector3::UNIT_SCALE) * scl;
		}
		node->scale(scale);

	}
	//---------------------------------------------------------------------
	
}
